package com.itheima.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.dto.RequestParams;
import com.itheima.mapper.SearchMapper;
import com.itheima.pojo.Item;
import com.itheima.service.SearchService;
import com.itheima.vo.PageResult;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.itheima.constants.ItemIndexConstants.MAPPING_TEMPLATE;

@Service
public class SearchServiceImpl extends ServiceImpl<SearchMapper, Item> implements SearchService {

    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private SearchService searchService;

    @Override
    public PageResult search(RequestParams requestParams) throws IOException {
        System.out.println("requestParams = " + requestParams);
        //获得查询结果
        SearchResponse response = getSearchResponse(requestParams);
        //解析查询结果
        PageResult pageResult = getPageResult(response);
        return pageResult;
    }

    private PageResult getPageResult(SearchResponse response) {
        PageResult pageResult = new PageResult();
        List<Item> items = new ArrayList<>();

        SearchHits responseHits = response.getHits();
        long total = responseHits.getTotalHits().value;
        pageResult.setTotal(total);

        SearchHit[] hits = responseHits.getHits();
        for (SearchHit hit : hits) {
            Item item = JSON.parseObject(hit.getSourceAsString(), Item.class);
            item.setPrice(item.getPrice() * 100);
            items.add(item);
        }
        pageResult.setList(items);
        return pageResult;
    }

    private SearchResponse getSearchResponse(RequestParams params) throws IOException {
        String key = params.getKey();
        Integer page = params.getPage();
        Integer size = params.getSize();
        String sortBy = params.getSortBy();
        String category = params.getCategory();
        String brand = params.getBrand();
        Integer minPrice = params.getMinPrice();
        Integer maxPrice = params.getMaxPrice();

        SearchRequest request = new SearchRequest("tb_item");
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //如果有问题，就不适用isNotEmpty：return str == null || str.length() == 0;
        if (StringUtils.isNotEmpty(key)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", key));
        } else {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }
        //分类catetory（手机、曲面电视、拉杆箱、休闲鞋、休闲鞋、硬盘、真皮包）
        if (StringUtils.isNotEmpty(category)) boolQueryBuilder.must(QueryBuilders.termQuery("category", category));
        //品牌brand（希捷、小米、华为、oppo、尤妮佳、康佳、意尔康、新秀丽、长虹、锤子）
        if (StringUtils.isNotEmpty(brand)) boolQueryBuilder.should(QueryBuilders.termQuery("brand", brand));
        //价格下限
        if (minPrice != null) boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(minPrice));
        //价格上限
        if (maxPrice != null) boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(maxPrice));

        //广告加分
        FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(
                boolQueryBuilder,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD", 1)
                                , ScoreFunctionBuilders.weightFactorFunction(5))
                }
        );//.boostMode(CombineFunction.SUM)

        //按照什么排序（默认、销量、价格）
        if (sortBy != null && sortBy.equals("price")) {
            request.source().sort("price", SortOrder.ASC);
        } else if (sortBy != null && sortBy.equals("sold")) {
            request.source().sort("sold", SortOrder.DESC);
        }
//
        //高亮
        request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));

        request.source().query(functionScoreQueryBuilder);
        //页码,每页条数
        request.source().from((page - 1) * size).size(size);

        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        return response;
    }


    @Override
    public void initializeESData() throws IOException {
        try {
            //批量添加数据到es中
            Integer count = searchService.count(new QueryWrapper<>());
            int size = 1000;
            int times = count / size + 1;
            for (int i = 1; i <= times; i++) {
                Page page = searchService.page(new Page<>(i, size), new QueryWrapper<>());
                List<Item> itemList = page.getRecords();
                System.out.println("第" + i + "遍，1000条");
                if (itemList.isEmpty()) break;
                BulkRequest request = new BulkRequest("tb_item");
                itemList.forEach(item -> {
                    String json = JSON.toJSONString(item);
                    request.add(new IndexRequest("tb_item").id(item.getId().toString()).source(json, XContentType.JSON));
                });
                client.bulk(request, RequestOptions.DEFAULT);
            }
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            client.close();
        }
    }



    @Override
    public void addDeleteItem(Item item) throws IOException {
        Integer status = item.getStatus();
        if (status == 2) {
            DeleteRequest request = new DeleteRequest("tb_item", item.getId().toString());
            client.delete(request, RequestOptions.DEFAULT);
        } else if (status == 1) {
            IndexRequest request = new IndexRequest("tb_item").id(item.getId().toString());
            String json = JSON.toJSONString(item);
            request.source(json, XContentType.JSON);
            client.index(request, RequestOptions.DEFAULT);
        }
    }

    @Override
    public void initializeESIndex() throws IOException {
        //创建Request对象
        CreateIndexRequest request = new CreateIndexRequest("tb_item");
        //准备DSL语句
        request.source(MAPPING_TEMPLATE, XContentType.JSON);
        //发送请求
        client.indices().create(request, RequestOptions.DEFAULT);
    }

}
